#pragma once

#include <string>
#include <memory>
#include <sstream>
#include <utility>
#include <iostream>
#include <vector>
#include <map>
#include <type_traits>
#include "cJson.h"
#include "reflect.hpp"
#include "reflect_enum.hpp"
#include "jsoncpp/include/json/json.h"

#ifdef __GNUC__
#include <string.h>
#endif



namespace reflect_json {
    /*
     *输入:const cJSON*类型，该类型中的数据形式为json格式
     *返回:返回一串string
     *创建 Json::StreamWriterBuilder 对象，这个对象用于配置和创建 Json::StreamWriter 对象，后者负责将 const cJSON* 对象转换为字符串
     *创建 Json::StreamWriter 对象，使用 builder.newStreamWriter() 方法创建一个 Json::StreamWriter 的实例writer:
     *  将其包装在 std::unique_ptr 智能指针中。这样做的好处是自动管理 Json::StreamWriter 对象的生命周期，避免内存泄漏
     *创建输出流:std::ostringstream os;: 创建一个 std::ostringstream 对象 os，用于构建和存储最终的字符串输出
     *将root中的数据写入os
     * 调用os.str()，将整个内容转化为字符串并返回
     * */
    inline std::string jsonToStr(cJSON* root) {
        char* jsonString = cJSON_PrintUnformatted(root);
        std::string result(jsonString);
        cJSON_free(jsonString);
        return result;
    }
    /*
     * 输入:string格式的字符串
     * 返回:const cJSON*,这个数据类型中的数据是json格式
     * 声明一个 const cJSON* 类型的变量 root，它将用于存储解析后的 JSON 值
     * 创建一个 Json::Reader 类的实例 reader。Json::Reader 是 JsonCpp 库中用于解析 JSON 字符串的类
     * 调用reader中的方法，将str转化为json并返回
     * */
    inline cJSON* strToJson(const std::string &json) {
        return cJSON_Parse(json.c_str());
    }


    /*
     * 模板类
     * 类中有一个静态变量value = false
     * */
    template<class T>
    struct special_traits {
        static constexpr bool value = false;
    };

    template<class T, std::enable_if_t<!reflect::has_member<T>() && !special_traits<T>::value && std::is_enum_v<T>, int>  = 0>
    cJSON* objToJson(T const &object) {
        cJSON* json_value = cJSON_CreateString(reflect_enum::get_enum_name(object).c_str());
        return json_value;
    }

    template<class T, std::enable_if_t<!reflect::has_member<T>() && !special_traits<T>::value && !std::is_enum_v<T>, int>  = 0>
    cJSON* objToJson(T const &object) {
        cJSON *json_value = nullptr;
        if constexpr (
                std::is_same_v<T, char>
                || std::is_same_v<T, unsigned char>
                || std::is_same_v<T, short>
                || std::is_same_v<T, unsigned short>
                || std::is_same_v<T, int>
                || std::is_same_v<T, unsigned int>
                || std::is_same_v<T, long>
                || std::is_same_v<T, unsigned long>
                || std::is_same_v<T, float>
                || std::is_same_v<T, double>
                ) {
            json_value = cJSON_CreateNumber(object);
            return json_value;
        }
        if constexpr (std::is_same_v<T, bool>) {
            json_value = cJSON_CreateBool(object);
            return json_value;
        }

        if constexpr (std::is_same_v<T, std::string>) {
            json_value = cJSON_CreateString(object.c_str());
            return json_value;
        }

        if constexpr (std::is_array_v<T> && std::is_same_v<std::decay_t<std::remove_extent_t<T>>, char>) {  // ???????????
            json_value = cJSON_CreateString(std::string(object).c_str());
            return json_value;
        }

        else if constexpr ((std::is_array_v<T>) &&(
                std::is_same_v<std::decay_t<std::remove_extent_t<T>>, short>
                || std::is_same_v<std::decay_t<std::remove_extent_t<T>>, unsigned short>
                || std::is_same_v<std::decay_t<std::remove_extent_t<T>>, int>
                || std::is_same_v<std::decay_t<std::remove_extent_t<T>>, unsigned int>
                || std::is_same_v<std::decay_t<std::remove_extent_t<T>>, float>
                || std::is_same_v<std::decay_t<std::remove_extent_t<T>>, double>
        )) {
            json_value = cJSON_CreateArray();
            constexpr size_t size = std::extent_v<std::decay_t<T>>;
            for (size_t i = 0; i < size; ++i) {
                cJSON_AddItemToArray(json_value, cJSON_CreateNumber(object[i]));
            }
        }


        if (json_value == nullptr) {
            const char* type_name = typeid(T).name();
            cJSON_AddStringToObject(json_value, "value", type_name);
        }

        return json_value;
    }




    template<class T, std::enable_if_t<!reflect::has_member<T>() && special_traits<T>::value, int>  = 0>
    const cJSON* objToJson(T const &object) {
        return special_traits<T>::objToJson(object);
    }

    template<class T, std::enable_if_t<reflect::has_member<T>(), int>  = 0>
    cJSON* objToJson(T const &object) {
        cJSON* root = cJSON_CreateObject(); // Initialize the root JSON object
        if (!root) return nullptr;

        reflect::foreach_member(object, [&](const char* key, auto& value) {
            // Recursively convert each member to JSON
            cJSON* json_value = objToJson(value);
            if (json_value) {
                cJSON_AddItemToObject(root, key, json_value);
            }
        });

        return root;
    }


    template<class T, std::enable_if_t<!reflect::has_member<T>() && !special_traits<T>::value && std::is_enum_v<T>, int>  = 0>
    T jsonToObj(const cJSON* root) {
        return (T)(reflect_enum::enum_from_name<T>(std::string(root->valuestring)));
    }

    template<class T, std::enable_if_t<!reflect::has_member<T>() && !special_traits<T>::value && !std::is_enum_v<T>, int>  = 0>
    T jsonToObj(const cJSON* root) {

        if constexpr (std::is_same_v<T, std::string>) {
            return std::string(root->valuestring);
        }

        if constexpr (std::is_same_v<T, int>
                      || std::is_same_v<T, unsigned int>
                      || std::is_same_v<T, short>
                      || std::is_same_v<T, unsigned short>
                      || std::is_same_v<T, long>
                      || std::is_same_v<T, unsigned long>
                      || std::is_same_v<T, float>
                      || std::is_same_v<T, double>
                ) {
            if (cJSON_IsNumber(root)){
                return static_cast<T>(root->valuedouble);
            }
        }

        if constexpr (std::is_same_v<T, char>
                      || std::is_same_v<T, unsigned char>
                ) {
            return static_cast<T>(root->valueint);
        }
        if constexpr (std::is_array_v<T> && std::is_same_v<std::decay_t<std::remove_extent_t<T>>, char>) {  // ???????????
            return static_cast<T>(root->valuestring);
        }
        if constexpr (std::is_same_v<T, std::string>) {
            if (cJSON_IsNumber(root)){
                return std::string(root->valuestring);
            }
        }
        if constexpr (std::is_same_v<T, bool>) {
            if (cJSON_IsBool(root)){
                return static_cast<T>(root->valuedouble);
            }
        }
        printf("Unsupported type: %s\n", typeid(T).name());
        return T();
    }


    template <class T, std::enable_if_t<!reflect::has_member<T>() && special_traits<T>::value, int> = 0>
    T jsonToObj(const cJSON* root) {
        return special_traits<T>::jsonToObj(root);
    }

    template <class T, std::enable_if_t<reflect::has_member<T>(), int> = 0>
    T jsonToObj(const cJSON *root) {
        T object;
        reflect::foreach_member(object, [&](const char *key, auto &value) {

            if constexpr (std::is_same_v<std::decay_t<decltype(value)>, char*>) [[unlikely]]{
                strcpy(value, cJSON_GetObjectItem(root, key)->valuestring);
            } else {

                value = jsonToObj<std::decay_t<decltype(value)>>(cJSON_GetObjectItem(root, key));
            }
        });
        return object;
    }

    template <class T, class Alloc>
    struct special_traits<std::vector<T, Alloc>> {
        static constexpr bool value = true;
        static cJSON* objToJson(std::vector<T, Alloc> const &object) {
            cJSON* jsonArray = cJSON_CreateArray();
            for (const auto& elem : object) {
                // Convert element to cJSON
                cJSON* jsonElem = reflect_json::objToJson(elem);
                // Add the cJSON element to the array
                cJSON_AddItemToArray(jsonArray, jsonElem);
            }
            return jsonArray;
        }

        static std::vector<T, Alloc> jsonToObj(const cJSON*  root) {
            std::vector<T, Alloc> object;
            if (cJSON_IsArray(root)) {
                cJSON* elem = root->child;
                while (elem != nullptr) {
                    // Convert each cJSON element to type T and add to vector
                    object.push_back(reflect_json::jsonToObj<T>(elem));
                    elem = elem->next;
                }
            }
            return object;
        }
    };

    template <class K, class V, class Alloc>
    struct special_traits<std::map<K, V, Alloc>> {
        static constexpr bool value = true;

        static cJSON* objToJson(std::map<K, V, Alloc> const &object) {
            cJSON* root = cJSON_CreateObject();  // 创建一个空的 JSON 对象

            for (const auto& elem : object) {
                // 这里需要根据类型来决定如何序列化值
                cJSON* json_value = reflect_json::objToJson(elem.second);  // 假设 reflect_json 已正确定义

                if (json_value) {
                    cJSON_AddItemToObject(root, elem.first.c_str(), json_value);
                }
            }

            return root;
        }

        static std::map<K, V, Alloc> jsonToObj(const cJSON*  root) {
            std::map<K, V, Alloc> object;

            for (int j = 0; j < cJSON_GetArraySize(root); j++) {
                cJSON* item = cJSON_GetArrayItem(root, j);
                if (item->type != cJSON_Object
                    && item->type != cJSON_Array
                    && !cJSON_IsNull(item)) {
                    if(item->type == cJSON_String){
                        object[item->string] = item->valuestring;
                    }else if (item->type == cJSON_Number){
                        object[item->string] = item->valuedouble;
                    }
                }
            }
            return object;
        }
    };


    template <class T>
    std::string serialize(T const &object) {
        cJSON* obj = objToJson(object);
        std::string str = jsonToStr(obj);
        cJSON_Delete(obj);
        return str;
    }

    template <class T>
    T deserialize(std::string const &json) {
        auto obj = strToJson(json);
        T struct_node = jsonToObj<T>(obj);
        cJSON_Delete(obj);
        return struct_node;
    }
}
